#include <stdio.h>
#include <math.h>
#include <time.h>
#include <limits.h>
#include "rand_bm.h"

#define max(a,b) a>b? a : b
#define NUM_THREADS 4

// Variaveis globais
double S, E, r, sigma, T;
int M;
double *trial;
double sum, sum2;
struct BoxMullerState state;

// métodos para thread
void *calc_trial(void *t);
void *calc_sum(void *t);
void *calc_sum2(void *t);

// inicialização do programa
int main(int argc, char *argv[]) {

	// verefica entrada do programa
	if (argc != 2) {
		printf("usage: ./black_scholes (0 = sequential / 1 = parallel)\n");
		return -1;
	}

	initBoxMullerState(&state);
	double tmp;
	int i;
	double rand; 
	scanf("%lf", &S);
	scanf("%lf", &E);
	scanf("%lf", &r);
	scanf("%lf", &sigma);
	scanf("%lf", &T);
	scanf("%d", &M);

	// aloca o espaço correto para o trial
	trial = (double *) malloc(M*sizeof(double));

	// INICIO DO PROCESSO EM SEQUENCIAL
	if (argv[1][0] == '0') {

		// executa sequencialmente o 'for' de forma a setar todos os valores de trial
		for(i=0; i<M; ++i) {
			rand = boxMullerRandom(&state);
			rand -= abs(rand);
			tmp = S * exp((r - (sigma*sigma)/2)*T + sigma * sqrt(T) * rand);
			trial[i] = (exp(-r*T)) * (max(tmp - E, 0));
		}

		// inicializa a somatória de trial e a somatória do trial ao quadrado
		sum = trial[0];
		sum2 = 0;
		
		// realiza a somatória de trial e trial ao quadrado
		for(i=1; i<M; ++i) {
			sum += trial[i];
			sum2 += (trial[i]*trial[i]);
		}			

	// INICIO DO PROCESSO EM PARALIZADO
	} else {

		// inicializa threads
		pthread_t trial_t[NUM_THREADS];
		pthread_t sum_t[NUM_THREADS];
		int t;
		int ret;
		void *status;

		// atribui cada thread para realizar uma parte dos calculos do trial
		for(t=0; t<NUM_THREADS; t++) {
			ret = pthread_create( &trial_t[t], NULL, calc_trial, (void *) t);
			if (ret) {
				printf("ERRO; return pthread_create(): %d\n", ret);
         			return -1;
			}
		}

		// espera todas as threads terminarem de calcular o trial para continuar o programa
		for(t=0; t<NUM_THREADS; t++) {
			ret = pthread_join(trial_t[t], &status);
			if (ret) {
				printf("ERRO; return pthread_join() : %d\n", ret);
				return -1;
			}
		}

		// valores iniciais para realizar a somatoria do trial
		sum = trial[0];
		sum2 = 0;


		// realiza a paralelização da somatória do trial
		for(t=0; t<NUM_THREADS; t++) {
			ret = pthread_create( &sum_t[t], NULL, calc_sum, (void *) t);
			if (ret) {
				printf("ERRO; return pthread_create(): %d\n", ret);
         			return -1;
			}
		}

		// espera toda a somatoria acabar de processar
		for(t=0; t<NUM_THREADS; t++) {
			ret = pthread_join(sum_t[t], &status);
			if (ret) {
				printf("ERRO; return pthread_join() : %d\n", ret);
				return -1;
			}
		}

	}

	// realiza calculos finais para achar 'confmin' e 'confmax'
	double mean = sum / M;
	double stddev = sqrt((sum2/(M-1)) - ((M/(M-1))*mean*mean));
	double confwidth = (1.96 * stddev) / sqrt(M);
	double confmin = mean - confwidth;
	double confmax = mean + confwidth;

	// printa valores e resultados
	printf("S\t%.lf\n", S);
	printf("E\t%.lf\n", E);
	printf("r\t%.lf\n", r);
	printf("sigma\t%.lf\n", sigma);
	printf("T\t%.lf\n", T);
	printf("M\t%d\n", M);
	printf("Confidence interval: (%.4f, %.3f)\n", confmin, confmax);

	// libera a memória alocada para o trial
	free(trial);

	return 0;
}

// thread para a realização do calculo do trial
void *calc_trial(void *t){
	
	double tmp;
	int i;
	double rand; 
	int n = (int *) t;
	
	// realiza o calculo do 'total de trials'/'numero de threads'
	for(i= n * M/NUM_THREADS; i<(n+1)*M/NUM_THREADS; i++) {
		rand = boxMullerRandom(&state);
		rand -= abs(rand);
		tmp = S * exp((r - (sigma*sigma)/2)*T + sigma * sqrt(T) * rand);
		trial[i] = (exp(-r*T)) * (max(tmp - E, 0));
	}

	// finaliza a thread
	pthread_exit((void *) t);
}


// thread para a realização do calculo do somatório
void *calc_sum(void *t){
	
	int i;
	int n = (int *) t;

	// realiza a somatória de 'total de trials'/'numero de threads'
	for(i= n * M/NUM_THREADS; i<(n+1)*M/NUM_THREADS; i++) {
		sum += trial[i];
		sum2 += (trial[i]*trial[i]);
	}	
	// finaliza a thread
	pthread_exit((void *) t);
}

		